package com.zzyl.nursing.service.impl;

import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.huaweicloud.sdk.iotda.v5.IoTDAClient;
import com.huaweicloud.sdk.iotda.v5.model.*;
import com.zzyl.common.constant.CacheConstants;
import com.zzyl.common.core.domain.AjaxResult;
import com.zzyl.common.utils.DateUtils;
import com.zzyl.common.utils.uuid.UUID;
import com.zzyl.nursing.dto.DeviceDto;
import com.zzyl.nursing.vo.DeviceDetailVo;
import com.zzyl.nursing.vo.ProductVo;
import lombok.Builder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import com.zzyl.nursing.mapper.DeviceMapper;
import com.zzyl.nursing.domain.Device;
import com.zzyl.nursing.service.IDeviceService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.transaction.annotation.Transactional;

import static com.huaweicloud.sdk.iotda.v5.IoTDAMeta.addDevice;

/**
 * 设备Service业务层处理
 *
 * @author zhangzhangzhang
 * @date 2025-09-22
 */
@Service
public class DeviceServiceImpl extends ServiceImpl<DeviceMapper, Device> implements IDeviceService {
    @Autowired
    private DeviceMapper deviceMapper;
    @Autowired
    private IoTDAClient client;
    @Autowired
    private RedisTemplate<String, String> redisTemplate;


    /**
     * 查询设备列表
     *
     * @param device 设备
     * @return 设备
     */
    @Override
    public List<Device> selectDeviceList(Device device) {

        return deviceMapper.selectDeviceList(device);
    }


    // 同步产品列表
    @Override
    public void syncProductList() {
        //1.从华为云查询设备信息
        ListProductsRequest request = new ListProductsRequest();
        request.setLimit(50);
        //通过IoTDA客户端发送请求获取产品列表
        ListProductsResponse listProductsResponse = client.listProducts(request);
        //获取产品列表信息
        List<ProductSummary> products = listProductsResponse.getProducts();

        //2.把信息存储到redis
        //只要是把数据存入到Redis中，考虑两件事就够了 :1.数据类型  2.key叫啥
        //redis是key-value结构   用value   ,key:iot:productsList
        //因为key-value 是String  所以这个序列化成json字符串
        redisTemplate.opsForValue().set(CacheConstants.ALL_PRODUCT_LIST_KEY, JSONUtil.toJsonStr(products));

    }

    // 查询所有产品列表
    @Override
    public List<ProductVo> allProduct() {
        //去哪查???? 从redis中查询数据   因为redis中key-value存的都是String 所以这返回的是原来的普通的String
        String json = redisTemplate.opsForValue().get(CacheConstants.ALL_PRODUCT_LIST_KEY);
        //如果数据为空，则返回一个空集合
        if (StrUtil.isBlank(json)) {
            return null;
        }
        /*
        json本质就是一个字符串
         从redis中取出字符串后 将这个字符串反序列化成ProductVo对象的列表 返回
         为啥能直接反序列化 ???
         是因为redis中存的字段跟ProductVo对象的字段一致并且类型也一致，所以可以直接反序列化成ProductVo对象的列表
        */
        return JSONUtil.toList(json, ProductVo.class);
    }


    // 注册设备
    @Override
    public void registerDevice(DeviceDto dto) {
        //判断设备名称是否重复
        //sql：select count(*) from device where device_name = ?
        Long count1 = lambdaQuery().eq(Device::getDeviceName, dto.getDeviceName())
                .count();
        if (count1 != null && count1 > 0) {
            throw new RuntimeException("设备名称重复");
        }

        //判断设备标识是否重复
        //sql: select count(*) from device where node_id = ?
        Long count2 = lambdaQuery().eq(Device::getNodeId, dto.getNodeId())
                .count();
        if (count2 != null && count2 > 0) {
            throw new RuntimeException("设备标识码重复");
        }

        //判断同一位置是否绑定相同产品
        // sql: select count(*) from device where binding_location = ? and location_type = ? and physical_location_type = ? and product_key = ?
        Long count3 = lambdaQuery().eq(Device::getBindingLocation, dto.getBindingLocation())
                .eq(Device::getLocationType, dto.getLocationType())
                .eq(Device::getPhysicalLocationType, dto.getPhysicalLocationType())
                .eq(Device::getProductKey, dto.getProductKey())
                .count();
        if (count3 != null && count3 > 0) {
            throw new RuntimeException("同一位置不能绑定相同产品");
        }

        //添加设备 一般先数据库加 再华为云加 但是由于secret没法获取 这个是华为云添加设备后自动生成的 所以先添加到华为云

        //华为云添加设备
        AddDeviceRequest request = new AddDeviceRequest();
        AddDevice body = new AddDevice();
        body.setNodeId(dto.getNodeId());
        body.setDeviceName(dto.getDeviceName());
        body.setProductId(dto.getProductKey());
        AuthInfo authInfo = new AuthInfo();
        //用hutool的IdUtil.fastSimpleUUID()方法生成一个随机的secret密钥
        String secret = IdUtil.fastSimpleUUID();
        authInfo.setSecret(secret);
        body.setAuthInfo(authInfo);
        request.setBody(body);

        //本来这个的在try catch块中，但是下面148行会报空指针异常 因为不能用这个局部变量
        AddDeviceResponse response = null;

        try {
            response = client.addDevice(request);
            if (response == null) {
                throw new RuntimeException("设备添加失败");
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

        //数据库添加设备
        //查看请求值 少了三个字段 iot_id secret ,hava_entrance_guard
        String deviceId = response.getDeviceId();
        Device device = BeanUtil.toBean(dto, Device.class);
        /*
        Device device1 = Device.builder().haveEntranceGuard(0).iotId(deviceId).secret(secret).build();
        * 这行作用和下面三行作用一样
        * */
        device.setHaveEntranceGuard(0);
        device.setIotId(deviceId);
        device.setSecret(secret);
        this.save(device);
    }


    //获取设备详细信息
    @Override
    public DeviceDetailVo queryDeviceDetail(String iotId) {
        //1.从数据库查询设备详细信息
        Device device = lambdaQuery()
                .eq(Device::getIotId, iotId)
                .one();
        if (device == null) {
            throw new RuntimeException("设备不存在");
        }

        //发现返回结果中缺少设备状态跟激活时间  这两字段只能从华为云中查询
        //2.从华为云查询设备状态跟激活时间
        ShowDeviceRequest request = new ShowDeviceRequest();
        request.withDeviceId(iotId);
        ShowDeviceResponse response = client.showDevice(request);
        String status = response.getStatus();
        String activeTime = response.getActiveTime();
        DeviceDetailVo vo = BeanUtil.toBean(device, DeviceDetailVo.class);
        vo.setDeviceStatus(status);
        if (activeTime != null) {
            // 定义解析格式
            DateTimeFormatter formatter = DateTimeFormatter.ISO_DATE_TIME;
            // 1. 解析为UTC时区的ZonedDateTime
            ZonedDateTime utcDateTime = ZonedDateTime.parse(activeTime, formatter);
            // 2. 转换为东八区(Asia/Shanghai)的时间
            ZonedDateTime shanghaiDateTime = utcDateTime.withZoneSameInstant(ZoneId.of("Asia/Shanghai"));
            // 3. 提取为LocalDateTime
            LocalDateTime localDateTime = shanghaiDateTime.toLocalDateTime();
            vo.setActiveTime(localDateTime);
        }
        return vo;
    }


    //查询设备上报数据 设备影子
    @Override
    public AjaxResult queryServiceProperties(String iotId) {
        //调用华为云的影子接口
        ShowDeviceShadowRequest request = new ShowDeviceShadowRequest();
        request.withDeviceId(iotId);
        ShowDeviceShadowResponse response = client.showDeviceShadow(request);
        System.out.println("response = " + JSONUtil.toJsonStr(response));

        //这个eventTime是UTC时间并且还是这种格式"20250922T130904Z"
        String eventTime = response.getShadow().get(0).getReported().getEventTime();
        // 定义输入格式：UTC时间格式 "yyyyMMdd'T'HHmmss'Z'"
        DateTimeFormatter inputFormatter = DateTimeFormatter.ofPattern("yyyyMMdd'T'HHmmss'Z'");

        // 解析UTC时间字符串为LocalDateTime
        LocalDateTime utcDateTime = LocalDateTime.parse(eventTime, inputFormatter);

        // 将UTC时间转换为ZonedDateTime（UTC时区）
        ZonedDateTime utcZoned = utcDateTime.atZone(ZoneId.of("UTC"));

        // 转换到东八区（Asia/Shanghai）
        ZonedDateTime cstZoned = utcZoned.withZoneSameInstant(ZoneId.of("Asia/Shanghai"));

        // 定义输出格式："yyyy-MM-dd HH:mm:ss"
        DateTimeFormatter outputFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

        // 格式化并返回结果
        String result = cstZoned.format(outputFormatter);
        System.out.println("转换后的时间：" + result);

        //因为运行时本质上这个object是LinkedHashMap类型，所以才能强转，通过debug看到的
        LinkedHashMap<String, Object> properties = (LinkedHashMap<String, Object>) response.getShadow().get(0).getReported().getProperties();

        //遍历：foreach、迭代器iterator、keySet
        //此处遍历循环原因是因为每一个设备的参数不一样 不能写死,所以循环并且每个参数都是key-value结构 用map存储并放到list中返回
        //(此处也可以 List<deviceVo> list = new ArrayList<>();)
        List<Map<String, Object>> list = new ArrayList<>();
        properties.forEach((k, v) -> {
            Map<String, Object> map = new HashMap<>();
            list.add(map);
            map.put("functionId", k);
            map.put("value", v);
            map.put("eventTime", result);
        });
        return AjaxResult.success(list);
    }


    //修改设备信息 ---只改设备名字,带着IOT_ID即可
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateDevice(DeviceDto dto) {
        //修改设备信息  ---第一步修改数据库里的设备名字  第二步修改华为云里的设备名字
        //修改数据库里的设备名字
        Long deviceId = dto.getId();
        //sql:select * from device where id = ?
        Device device = lambdaQuery().eq(Device::getId, deviceId).one();
        if (device == null) {
            throw new RuntimeException("设备不存在");
        }
        device.setDeviceName(dto.getDeviceName());
        this.updateById(device);

        //修改华为云里的设备名字
        UpdateDeviceRequest request = new UpdateDeviceRequest();
        request.setDeviceId(device.getIotId());
        UpdateDevice body = new UpdateDevice();
        body.setDeviceName(dto.getDeviceName());
        request.setBody(body);
        try {
            UpdateDeviceResponse response = client.updateDevice(request);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }


    //删除设备
    @Override
    public void removeDevice(String iotId) {
        //删除设备信息  ---第一步删除数据库里的设备名字  第二步删除华为云里的设备名字

        //第一步删除数据库里的设备名字
        //先根据iotId查出来 判断是否为空 不是空就删除
        //sql:select * from device where iot_id = ?
        Device device = lambdaQuery().
                eq(Device::getIotId, iotId)
                .one();
        if (device == null) {
            throw new RuntimeException("设备不存在");
        }
        this.removeById(device.getId());

        //第二步删除华为云里的设备

        DeleteDeviceRequest request = new DeleteDeviceRequest();
        request.withDeviceId(iotId);
        try {
            DeleteDeviceResponse response = client.deleteDevice(request);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

    }


}
